from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from django_redis import get_redis_connection

from goods.models import SKU
from .serializers import CartSerialzier, CartSKUSerializer,CartDeleteSerializer
from rest_framework.response import Response
import base64
import pickle
from rest_framework.views import status

"""
购物车功能实现需求:用户不管有没有登陆,都可以将商品添加到购物车
post方式传递参数,参数为:商品id,是否勾选selected,数量count
对于已经登陆的用户,将数据保存在redis中,对于没有登陆的用户,数据保存在cookie中
redis保存方式:哈希  key:cart_userid. value:{sku_id:count,skuid:count...}
set key: cart_selected_userid. value:{sku_id,sku_id...}
cookie 保存方式: {sku_id:{ "count":XXX, "selected:false
}....
}
pickle.dumps() 将python数据序列化为bytes类型
pickle.loads() 将bytes类型数据反序列化为python的数据类型

base64.b64encode() 将bytes类型数据进行base64编码，返回编码后的bytes类型
base64.b64deocde() 将base64编码的bytes类型进行解码，返回解码后的bytes类型

"""

class CartView(APIView):
    """
    POST  /carts/   实现购物车的添加功能
    """
    # 如果用户没有传递token或者传递的正确的token,都可以执行post方法
    # 如果用户伪造token,不能添加到购物车,所以我需要先将验证去掉
    # 我们重写 对用户的认证方法 就可以让伪造的token进入到 添加购物车的功能

    def perform_authentication(self, request):
        pass

    def post(self, request):
        """
        增加数据
        # 1. 先提交数据, 后端要接受到数据
        # 2. 接收数据之后对数据进行校验
        # 3. 获取到校验的数据之后,根据用户的登录状态 进行处理
        # 4. 登录用户保存在Redis
        # 5. 非登录用户保存在cookie中

        """

        # 因为我们重写了 perform_authentication 所以我们获取用户信息的时候
        # 对于伪造的用户信息 获取可能会存在异常,所以要捕获一下

        # 1. 先提交数据, 后端要接受到数据 (sku_id,count,selected)
        # 2. 接收数据之后对数据进行校验
        serializer = CartSerialzier(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 3. 获取到校验的数据之后,根据用户的登录状态 进行处理
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.data.get('selected')

        # 对于伪造的用户信息，可能会出现异常，所以要捕获一下
        try:
            user = request.user
        except Exception:
            user = None

        # 3.根据用户登陆状态进行处理
        # 先判断用户是否为登陆用户
        # request.user.is_authenticated  是否为登陆用户
        if user is not None and user.is_authenticated:
            # 登陆用户保存在redis
            # 链接redis
            redis_conn = get_redis_connection("cart")

            # 保存数据
            # sku_id: count   哈希
            # sku_id    set   是否选中
            # 添加redis的时候,有可能之前已经存在了该商品
            # redis_conn.hset("cart_%s"%user.id,sku_id,count)

            # hincrby 如果 redis很中没有值,则添加此数据
            # hincrby 如果 redis很有值,则累加此数据
            # redis_conn.hincrby("cart_%s"%user.id,sku_id,count)
            #
            # if selected:
            #     redis_conn.sadd("cart_selected_%s"%user.id,sku_id)
            #

            # redis管道技术
            # 1 创建管道实例
            pl = redis_conn.pipeline()
            # 2 通过管道实例操作redis
            pl.hincrby("cart_%s" % user.id, sku_id, count)

            if selected:
                pl.sadd("cart_selected_%s" % user.id, sku_id)

            # 3. 让管道实例区执行
            pl.execute()

            return Response(serializer.data)


        # 5.非登陆用户,保存在cookie
        else:
            # cookie
            # 保存方式: {sku_id: {"count": XXX, "selected:false
            #                 }....
            #        }

            cookie_cart = request.COOKIES.get('cart') # 读取cookie中的数据

            if cookie_cart is not None:
                # cookie中有购物车数据
                decode = base64.b64decode(cookie_cart)

                cart = pickle.loads(decode)

            else:
                # 购物车没有数据
                cart = {}

                # 更新数据,判断商品是否存在,存在累加个数,不存在直接添加商品
                # sku_id: {count:xxx,selected:xxx}
                # {1:{'count':5,'selected':True},2:{'count':5,'selected':True},...}

            if sku_id in cart:
                    # 存在累加个数
                    # 获取原来个数
                    orginal_count = cart[sku_id]['count']
                    # 累加
                    count += orginal_count
                # 如果有更新数据,没有直接添加

            cart[sku_id]={
                    "count":count,
                    'selected':selected
                }


                # 返回响应
            response = Response(serializer.data)

                # 先将数据转换成bytes类型
            dumps = pickle.dumps(cart)
                # base64.b64encode() 返回的bytes类型,所以要进行decode()操作
            cookie_str = base64.b64encode(dumps).decode()

            response.set_cookie('cart',cookie_str,3600*24)

            return response



    def get(self,request):
        """
        1.购物车信息展示.对用户进行判断
        2.判断用户的登陆状态,登陆用户获取redis数据,未登陆用户获取cookie数据
        3.查询商品模型,进行序列化操作,获取id数据,查询商品信息[redis和cookie中数据结构要一致]
        4.将查询的信息转换为json数据

        """
        # 根据用户的信息判断:
        try:
            user = request.user
        except Exception:
            user = None

        # 判断用户的登陆状态:
        if user is not None and user.is_authenticated:
            # 登陆用户,获取redis数据
            # 连接redis
            redis_conn = get_redis_connection('cart')

            # 获取所有数据
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            # hash {sku_id:count,sku_id:count}

            redis_selected_ids = redis_conn.smembers('cart_selected_%s' % user.id)
            # [sku_id,sku_id]


            # 2.3 拼接成 cookie的数据结构
            # cart = {sku_id:{'count':xxx,'selected':xxxx},...}
            cart = {}
            # 对 redis_cart 进行遍历, 因为 redis-cart 里边存放了所有数据
            for sku_id, count in redis_cart.items():

                if sku_id in redis_selected_ids:
                    selected = True
                else:
                    selected = False

                cart[int(sku_id)] = {
                    'count': int(count),
                    # 'selected':sku_id in redis_selected_ids
                    'selected': selected
                }

        else:
            # 获取cookie中的数据
            cookie_cart = request.COOKIES.get('cart')

            if cookie_cart is not None:
                cart = pickle.loads( base64.b64decode(cookie_cart))
            else:
                cart = {}

        # 4. 查询商品数据,进行序列化操作
        # cart
        # cart = {sku_id:{'count':xxx,'selected':xxxx},...}
        #      4.1 获取id信息
        ids = cart.keys()
        #　ids = [1,2,3...]

        # 查询数据库,根据id进程查询
        skus = SKU.objects.filter(id__in=ids)
        # 注意skus中的SKU没有count和selected数据

        for sku in skus:
            sku.count = cart[sku.id]['count']
            sku.selected = cart[sku.id]['selected']

        #     4.3 需要将模型转换为JSON
        serializer = CartSKUSerializer(skus, many=True)

        return Response(serializer.data)




    def put(self,request):
        """
        1. 提交数据 sku_id,count,selected
        2. 对数据进行校验
        3.判断用户的状态
        4.判读用户的登陆状态,登陆用户修改redis数据,非登陆用户修改cookie数据
        5.返回响应

        """
        serializer = CartSerialzier(data=request.data)
        serializer.is_valid()

        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 判断用户状态
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 登陆用户获取redis数据
            #　连接redis
            #  更新数据
            #　返回响应

            redis_conn = get_redis_connection('cart')

            #更新数据 ,幂等操作, 幂等操作是将最终的个数返回回来
            redis_conn.hset('cart_%s'%user.id,sku_id,count)

            # 更新勾选操作,如果勾选了,要添加到选中记录中
            if selected:
                redis_conn.sadd('cart_selected_%s'%user.id,sku_id)
            else:
                # 将可能存在的勾选记录删除
                redis_conn.srem('cart_selected_%s'%user.id,sku_id)

            # 4.3 返回响应
            # 注意,因为是采用的幂等,我们需要将服务器的最终结果返回回去
            return Response(serializer.data)

        else:
            # 非登陆用户更新cookie数据
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart is not None:
                cart = pickle.loads(base64.b64decode(cookie_cart))

            else:
                cart = {}

            # 更新数据,不管有没有都得走以下操作
            # if sku_id in cart:

            cart[sku_id] = {
                'count':count,
                'selected':selected
            }

            # 返回响应
            response = Response(serializer.data)

            cookie_str = base64.b64encode(pickle.dumps(cart)).decode()

            response.set_cookie('cart',cookie_str,24*3600)

            return response







        pass




    def delete(self,request):
        """
        1.获取数据,sku_id
        2.对数据进行校验
        3.判断用户状态
        4.判断用户登陆信息,登陆用户删除redis数据,非登陆用户删除cookie数据
        5.返回响应
        """
        serializer = CartDeleteSerializer(data=request.data)

        serializer.is_valid(raise_exception=True)

        sku_id = serializer.data.get('sku_id')

        # 判断用户状态
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 登陆用户删除redis数据
            # 连接redis
            redis_conn = get_redis_connection('cart')

            # 删除数据
            redis_conn.hdel("cart_%s"%user.id,sku_id)

            redis_conn.srem('cart_selected_%s'%user.id,sku_id)

            return Response(status=status.HTTP_204_NO_CONTENT)

        else:
            # 非登陆用户删除cookie数据
            cookie_cart = request.COOKIES.get('cart')

            if cookie_cart is not None:
                cart = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart = {}

            # 删除
            if sku_id in cart:
                del cart[sku_id]

            # 返回响应
            response = Response(status=status.HTTP_204_NO_CONTENT)

            cookie_str = base64.b64encode(pickle.dumps(cart)).decode()

            response.set_cookie('cart', cookie_str, 24 * 3600)


            return response










